﻿using System;
using System.Threading.Tasks;
using HslCommunication;
using DataCapture.Helper;
using System.Collections.Generic;
using System.Linq;
using HslCommunication.Profinet.Melsec;
using System.Diagnostics;
using System.Threading;

namespace DataCapture.DeviceDataSave;

/// <summary>
/// PLC数据通信基础类，负责PLC连接管理、数据采集及掉线重连
/// </summary>
public class PLCDataBase : IDisposable
{
    private readonly string _plcName;
    private readonly PublicHelp.PlcStatus _plcStatus;
    private MelsecMcNet _channels;
    private bool _isDisposed;

    // 状态标识（线程安全）
    private bool _deviceRunFlag; // 设备运行标志位
    public  bool _isConnected { get; private set; }  // 是否已连接
    private bool _isReconnecting; // 是否正在重连中
    private int _reconnectDelay = 5000; // 初始重连延迟（毫秒）
    private const int MaxReconnectDelay = 30000; // 最大重连延迟（毫秒）

    // 线程同步工具
    private readonly SemaphoreSlim _plcSemaphore = new SemaphoreSlim(1, 1);
    private readonly object _syncLock = new object();

    /// <summary>
    /// 构造函数
    /// </summary>
    public PLCDataBase(string plcName, string ip, int port, int[] portList = null)
    {
        _plcName = plcName ?? throw new ArgumentNullException(nameof(plcName));
        _plcStatus = new PublicHelp.PlcStatus
        {
            ip = ip ?? throw new ArgumentNullException(nameof(ip)),
            port = port,
            portList = portList,
            status = false,
            plcName = plcName,
            connectCount = 0
        };

        // 初始化连接
        InitializeConnection();
    }

    /// <summary>
    /// 初始化连接
    /// </summary>
    public void InitializeConnection()
    {
        lock (_syncLock)
        {
            if (_channels == null)
            {
                _channels = new MelsecMcNet(_plcStatus.ip, _plcStatus.port)
                {
                    ConnectTimeOut = 3000,
                    ReceiveTimeOut = 3000
                };

                // 配置多端口容错
                if (_plcStatus.portList != null && _plcStatus.portList.Length > 0)
                {
                    _channels.GetPipeSocket().SetMultiPorts(_plcStatus.portList);
                }
            }
        }

        // 启动连接任务
        StartConnectionTask();
    }

    /// <summary>
    /// 启动连接任务（确保同一时间只有一个连接任务）
    /// </summary>
    private void StartConnectionTask()
    {
        if (_isReconnecting) return;

        _isReconnecting = true;
        Task.Run(async () => await ConnectWithRetryAsync());
    }

    /// <summary>
    /// 带重试机制的连接方法
    /// </summary>
    private async Task ConnectWithRetryAsync()
    {
        const int maxRetries = 5;
        int retryCount = 0;

        try
        {
            while (retryCount < maxRetries && !_isDisposed)
            {
                // 替换原有的 using (await _plcSemaphore.LockAsync())
                await _plcSemaphore.WaitAsync();
                try
                {
                    if (_isConnected) break;

                    try
                    {
                        if (_channels == null)
                        {
                            Log4NetHelper.Debug($"【{_plcName}】通信对象未初始化，重新初始化", true);
                            InitializeConnection();
                            continue;
                        }

                        // 尝试连接
                        var connectResult = await _channels.ConnectServerAsync();
                        if (connectResult.IsSuccess)
                        {
                            OnConnectSuccess();
                            return;
                        }

                        // 连接失败处理
                        OnConnectFailed(connectResult.Message, retryCount, maxRetries);
                    }
                    catch (Exception ex)
                    {
                        OnConnectFailed(ex.Message, retryCount, maxRetries);
                    }
                }
                finally
                {
                    _plcSemaphore.Release();
                }
                retryCount++;
                if (retryCount < maxRetries)
                {
                    await Task.Delay(_reconnectDelay);
                }
            }

            // 达到最大重试次数仍失败
            if (!_isConnected && !_isDisposed)
            {
                Log4NetHelper.Debug($"【{_plcName}】经{maxRetries}次重试后仍连接失败，将延迟{_reconnectDelay / 1000}秒后重试", true);
                await Task.Delay(_reconnectDelay);
                // 采用退避策略增加延迟
                _reconnectDelay = Math.Min(_reconnectDelay * 2, MaxReconnectDelay);
                _isReconnecting = false;
                StartConnectionTask(); // 重新发起连接
            }
        }
        finally
        {
            _isReconnecting = false;
        }
    }

    /// <summary>
    /// 连接成功处理
    /// </summary>
    private void OnConnectSuccess()
    {
        _isConnected = true;
        _deviceRunFlag = true;
        _plcStatus.status = true;
        _plcStatus.connectCount = 0;
        _reconnectDelay = 5000; // 重置重连延迟
        

        Log4NetHelper.Debug($"【{_plcName}】 连接成功 - IP:{_plcStatus.ip}, 端口:{_plcStatus.port}", true);
    }

    /// <summary>
    /// 连接失败处理
    /// </summary>
    private void OnConnectFailed(string errorMsg, int retryCount, int maxRetries)
    {
        _isConnected = false;
        _deviceRunFlag = false;
        _plcStatus.status = false;

        Log4NetHelper.Debug($"【{_plcName}】连接失败({retryCount + 1}/{maxRetries})：{errorMsg}", true);
    }

    /// <summary>
    /// 断开并重新连接
    /// </summary>
    public void Reconnect()
    {
        if (_isDisposed) return;

        Task.Run(async () =>
        {
            // 替换原有的 using (await _plcSemaphore.LockAsync())
            await _plcSemaphore.WaitAsync();
            try
            {
                // 释放旧连接
                if (_channels != null)
                {
                    _channels.ConnectClose();
                    _channels.Dispose();
                    _channels = null;
                   
                }

                _isConnected = false;
                _deviceRunFlag = false;
                Log4NetHelper.Debug($"【{_plcName}】已断开旧连接，准备重新连接", true);

                // 初始化新连接
                InitializeConnection();
            }
            catch (Exception ex)
            {
                Log4NetHelper.Error($"【{_plcName}】重连过程异常：{ex.Message}\r\n{ex.StackTrace}", true);
                StartConnectionTask(); // 确保重连任务继续
            }
            finally
            {
                _plcSemaphore.Release();
            }
        });
    }

    /// <summary>
    /// 处理读取结果的通用方法
    /// </summary>
    private T HandleReadResult<T>(OperateResult<T> result, string address, int readSize) where T : class
    {
        if (result == null)
        {
            LogReadError(address, readSize, "空结果");
            return null;
        }

        if (result.IsSuccess)
        {
            if (!_deviceRunFlag)
            {
                _deviceRunFlag = true;
                _plcStatus.status = true;
                _plcStatus.connectCount = 0;
                Log4NetHelper.Debug($"【{_plcName}】恢复数据读取 - 地址:{address}, 长度:{readSize}", true);
            }
            return result.Content;
        }
        else
        {
            LogReadError(address, readSize, result.Message);
            return null;
        }
    }

    /// <summary>
    /// 记录读取错误并判断是否需要重连
    /// </summary>
    private void LogReadError(string address, int readSize, string errorMsg)
    {
        if (_deviceRunFlag)
        {
            _deviceRunFlag = false;
            _plcStatus.status = false;
            Log4NetHelper.Debug($"【{_plcName}】数据读取失败 - 地址:{address}, 长度:{readSize}, 错误:{errorMsg}", true);
        }

        _plcStatus.connectCount++;
        if (_plcStatus.connectCount >= 3)
        {
            _plcStatus.connectCount = 0;
            MainForm.Instance.ShowInfo($"【{_plcName}】连接异常，请检查设备状态");
            Reconnect(); // 触发重连
        }
    }

    #region 数据读取方法

    /// <summary>
    /// 同步批量读取Bool数组
    /// </summary>
    public OperateResult<bool[]> ReadBoolData(string address, ushort readSize)
    {
        return ExecuteReadOperation(() =>
        {
            var result = _channels.ReadBool(address, readSize);
            var content = HandleReadResult(result, address, readSize);
            return content != null
                ? OperateResult.CreateSuccessResult(content)
                : new OperateResult<bool[]>(result?.Message ?? "读取失败");
        }, address, readSize);
    }

    /// <summary>
    /// 同步读取16位的无符号整型数组（ushort）
    /// </summary>
    public OperateResult<ushort[]> ReadUInt16Data(string address, ushort readSize)
    {
        return ExecuteReadOperation(() =>
        {
            var result = _channels.ReadUInt16(address, readSize);
            var content = HandleReadResult(result, address, readSize);
            return content != null
                ? OperateResult.CreateSuccessResult(content)
                : new OperateResult<ushort[]>(result?.Message ?? "读取失败");
        }, address, readSize);
    }

    /// <summary>
    /// 同步读取16位的有符号整型数组（short）
    /// </summary>
    public OperateResult<short[]> ReadInt16Data(string address, ushort readSize)
    {
        return ExecuteReadOperation(() =>
        {
            var result = _channels.ReadInt16(address, readSize);
            var content = HandleReadResult(result, address, readSize);
            return content != null
                ? OperateResult.CreateSuccessResult(content)
                : new OperateResult<short[]>(result?.Message ?? "读取失败");
        }, address, readSize);
    }

    /// <summary>
    /// 同步批量读取32位的有符号整型数组
    /// </summary>
    public OperateResult<int[]> ReadInt32Data(string address, ushort readSize)
    {
        return ExecuteReadOperation(() =>
        {
            var result = _channels.ReadInt32(address, readSize);
            var content = HandleReadResult(result, address, readSize);
            return content != null
                ? OperateResult.CreateSuccessResult(content)
                : new OperateResult<int[]>(result?.Message ?? "读取失败");
        }, address, readSize);
    }

    /// <summary>
    /// 批量读取指定地址的PLC数据，返回字节数组
    /// </summary>
    public OperateResult<byte[]> RandomReadByName(string[] addresses, ushort[] lengths)
    {
        if (addresses == null || lengths == null || addresses.Length != lengths.Length)
            return new OperateResult<byte[]>("地址或长度数组无效");

        return ExecuteReadOperation(() =>
        {
            var result = _channels.ReadRandom(addresses, lengths);
            var content = HandleReadResult(result, $"批量地址({addresses.Length}个)", 0);
            return content != null
                ? OperateResult.CreateSuccessResult(content)
                : new OperateResult<byte[]>(result?.Message ?? "读取失败");
        }, "批量随机读取", addresses.Length);
    }

    /// <summary>
    /// 批量读取指定地址的PLC数据，返回字节数组（分批处理）
    /// </summary>
    public OperateResult<byte[]> RandomReadByName(string[] addresses)
    {
        if (addresses == null || addresses.Length == 0)
            return new OperateResult<byte[]>("地址数组为空");

        return ExecuteReadOperation(() =>
        {
            var batches = SplitToChunks(addresses, 150);
            var allData = new List<byte>();
            string errorMessage = string.Empty;

            foreach (var batch in batches)
            {
                var result = _channels.ReadRandom(batch.ToArray());
                if (!result.IsSuccess)
                {
                    errorMessage = result.Message;
                    break;
                }
                allData.AddRange(result.Content);
            }

            if (!string.IsNullOrEmpty(errorMessage))
            {
                LogReadError("批量读取（分批）", addresses.Length, errorMessage);
                return new OperateResult<byte[]>(errorMessage);
            }

            if (!_deviceRunFlag)
            {
                _deviceRunFlag = true;
                _plcStatus.status = true;
                _plcStatus.connectCount = 0;
                Log4NetHelper.Debug($"[{_plcName}] 恢复批量数据读取", true);
            }

            return OperateResult.CreateSuccessResult(allData.ToArray());
        }, "批量随机读取（分批）", addresses.Length);
    }

    /// <summary>
    /// 批量读取不连续的X、Y开头的IO地址
    /// </summary>
    public int[] BatchReadXY(string[] addresses)
    {
        var result = new int[addresses?.Length ?? 0];
        if (addresses == null || addresses.Length == 0)
            return result;

        try
        {
            _plcSemaphore.Wait();
            if (!_isConnected || _channels == null)
            {
                Log4NetHelper.Debug($"[{_plcName}] 未连接，无法读取XY地址", true);
                return result;
            }

            for (int i = 0; i < addresses.Length; i++)
            {
                var address = addresses[i];
                if (string.IsNullOrEmpty(address) || address == "无")
                {
                    result[i] = -1;
                    continue;
                }

                if (!address.StartsWith("X", StringComparison.OrdinalIgnoreCase) &&
                    !address.StartsWith("Y", StringComparison.OrdinalIgnoreCase))
                {
                    Log4NetHelper.Error($"[{_plcName}] 地址格式错误: {address}（必须以X或Y开头）", true);
                    result[i] = -1;
                    continue;
                }

                try
                {
                    var readResult = _channels.ReadBool(address);
                    result[i] = readResult.IsSuccess ? (readResult.Content ? 1 : 0) : -1;
                }
                catch (Exception ex)
                {
                    Log4NetHelper.Error($"[{_plcName}] 读取地址{address}异常: {ex.Message}", true);
                    result[i] = -1;
                    HandleReadException();
                }
            }
        }
        finally
        {
            _plcSemaphore.Release();
        }

        return result;
    }

    #endregion

    #region 辅助方法

    /// <summary>
    /// 执行读取操作的通用包装
    /// </summary>
    private OperateResult<T> ExecuteReadOperation<T>(Func<OperateResult<T>> readAction, string address, int readSize)
        where T : class
    {
        try
        {
            if (!_isConnected || _channels == null)
            {
                return new OperateResult<T>($"[{_plcName}] 未连接，无法读取 - 地址:{address}");
            }

            _plcSemaphore.Wait();
            return readAction();
        }
        catch (Exception ex)
        {
            var errorMsg = $"[{_plcName}] 读取异常 - 地址:{address}, 错误:{ex.Message}";
            Log4NetHelper.Error(errorMsg + $"\r\n{ex.StackTrace}", true);
            HandleReadException();
            return new OperateResult<T>(errorMsg);
        }
        finally
        {
            if (_plcSemaphore.CurrentCount == 0)
                _plcSemaphore.Release();
        }
    }

    /// <summary>
    /// 处理读取异常（统一计数和重连判断）
    /// </summary>
    private void HandleReadException()
    {
        if (_deviceRunFlag)
        {
            _deviceRunFlag = false;
            _plcStatus.status = false;
        }

        _plcStatus.connectCount++;
        if (_plcStatus.connectCount >= 3)
        {
            _plcStatus.connectCount = 0;
            MainForm.Instance.ShowInfo($"[{_plcName}] 连续读取失败，尝试重新连接");
            Reconnect();
        }
    }

    /// <summary>
    /// 分割数组为指定大小的批次
    /// </summary>
    private static IEnumerable<IEnumerable<T>> SplitToChunks<T>(IEnumerable<T> source, int chunkSize)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (chunkSize <= 0) throw new ArgumentOutOfRangeException(nameof(chunkSize));

        using var enumerator = source.GetEnumerator();
        while (enumerator.MoveNext())
        {
            var chunk = new List<T>();
            do
            {
                chunk.Add(enumerator.Current);
            } while (chunk.Count < chunkSize && enumerator.MoveNext());
            yield return chunk;
        }
    }

    #endregion

    #region 数据模拟（测试用）

    public OperateResult<ushort[]> TestReadUInt16Data(string address, ushort readSize)
    {
        return SimulateRead<ushort[]>(address, readSize, () =>
        {
            var data = new ushort[readSize];
            lock (_syncLock)
            {
                var random = new Random(address.GetHashCode());
                for (int i = 0; i < readSize; i++)
                {
                    data[i] = (ushort)random.Next(0, ushort.MaxValue + 1);
                }
            }
            return data;
        });
    }

    public OperateResult<int[]> TestReadInt32Data(string address, ushort readSize)
    {
        return SimulateRead<int[]>(address, readSize, () =>
        {
            var data = new int[readSize];
            lock (_syncLock)
            {
                var random = new Random(address.GetHashCode());
                for (int i = 0; i < readSize; i++)
                {
                    data[i] = random.Next();
                }
            }
            return data;
        });
    }

    public OperateResult<byte[]> TestInt32RandomReadByName(string[] addresses, ushort[] leg)
    {
        return SimulateBatchRead(addresses, () =>
        {
            var dataList = new List<byte>();
            foreach (var address in addresses)
            {
                lock (_syncLock)
                {
                    var random = new Random(address.GetHashCode());
                    dataList.AddRange(BitConverter.GetBytes(random.Next()));
                }
            }
            return dataList.ToArray();
        });
    }

    public OperateResult<byte[]> TestUInt16RandomReadByName(string[] addresses)
    {
        return SimulateBatchRead(addresses, () =>
        {
            var dataList = new List<byte>();
            foreach (var address in addresses)
            {
                lock (_syncLock)
                {
                    var random = new Random(address.GetHashCode());
                    dataList.AddRange(BitConverter.GetBytes((ushort)random.Next(0, ushort.MaxValue)));
                }
            }
            return dataList.ToArray();
        });
    }

    /// <summary>
    /// 模拟单地址读取
    /// </summary>
    private OperateResult<T> SimulateRead<T>(string address, int size, Func<T> dataGenerator) where T : class
    {
        try
        {
            var data = dataGenerator();
            return OperateResult.CreateSuccessResult(data);
        }
        catch (Exception ex)
        {
            return new OperateResult<T>($"模拟读取失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 模拟批量读取
    /// </summary>
    private OperateResult<byte[]> SimulateBatchRead(string[] addresses, Func<byte[]> dataGenerator)
    {
        try
        {
            var data = dataGenerator();
            return OperateResult.CreateSuccessResult(data);
        }
        catch (Exception ex)
        {
            return new OperateResult<byte[]>($"模拟批量读取失败: {ex.Message}");
        }
    }

    #endregion

    #region 资源释放

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_isDisposed) return;

        if (disposing)
        {
            // 释放托管资源
            _plcSemaphore.Dispose();
        }

        // 释放非托管资源
        if (_channels != null)
        {
            _channels.ConnectClose();
            _channels.Dispose();
            _channels = null;
           
        }

        _isDisposed = true;
        _isConnected = false;
        _deviceRunFlag = false;
        Log4NetHelper.Debug($"[{_plcName}] 已释放所有资源", true);
    }

    ~PLCDataBase()
    {
        Dispose(false);
    }

    #endregion
}